/*
 * ContentRecord.java
 *
 * Copyright(c) 2010
 * Michael Kroez
 * kroez@studiorutton.de
 *
 * All rights reserved.
 *
 * $Id$
 * 
 */

package de.studiorutton.gerengdic.databaseaccess;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;

import de.studiorutton.gerengdic.databaseaccess.DatabaseAccessException.ErrorCode;

public class ContentAccess {
	public final static byte CONTENT_RECORD_HEADER = 0x02;
	public final static int LANGUAGE_LEFT = 1;
	public final static int LANGUAGE_RIGHT = 2;
	public final static int LANGUAGE_BOTH = 3;
	private int DatabaseVersion;
	private RandomAccessFile raf;
	private String file;
	private ArrayList<LanguageAccess> languages;
	private RecordAccess records;

	// [ CONTENT_RECORD_HEADER | 4 bytes DatabaseVersion
	// | 4 bytes languageOneSize | nbytes languageOne | 4 bytes sirOne
	// StartPosition | 4 bytes sirOne EndPosition
	// | 4 bytes iprOne StartPosition | 4 bytes iprOne EndPosition
	// | 4 bytes lastViewOffset
	// 4 bytes languageTwoSize | nbytes languageTwo | 4 bytes sirTwo
	// StartPosition | 4 bytes sirTwo EndPositiong
	// | 4 bytes iprTwo StartPosition | 4 bytes iprTwo EndPosition
	// | 4 bytes lastViewOffset
	// 4 bytes record StartPosition | 4 bytes record EndPosition

	public ContentAccess(String file, RandomAccessFile raf) {
		this.file = file;
		this.raf = raf;
		languages = new ArrayList<LanguageAccess>();
	}

	public void close() throws DatabaseAccessException {
		records.close();
		for (LanguageAccess la : languages)
			la.close();
	}

	public LanguageAccess getLanguageAccess(int language) {
		return languages.get(language - 1); // Modified for changed language constants
	}

	public RecordAccess getRecordAccess() {
		return records;
	}

	public String toString() {
		int i;
		StringBuilder sb = new StringBuilder();
		for (i = 0; i < languages.size(); i++) {
			sb.append(languages.get(i));
			sb.append("\n");
		}

		return sb.toString();
	}

	public void readContentRecord() throws DatabaseAccessException {
		try {
			byte header = raf.readByte();
			if (header < 0 || header != CONTENT_RECORD_HEADER)
				throw new DatabaseAccessException( ErrorCode.ERROR_CANNOT_FIND_CONTENT_HEADER );
				
			DatabaseVersion = raf.readByte();

			// Language One
			int languageOneSize = raf.readInt();
			byte[] bufOne = new byte[languageOneSize];
			raf.read(bufOne, 0, languageOneSize);
			String languageOne = new String(bufOne, 0, languageOneSize,
					DatabaseAccess.CHARSET_DEFAULT);
			int superIndexOneBegin = raf.readInt();
			int superIndexOneEnd = raf.readInt();
			int indexPositionOneBegin = raf.readInt();
			int indexPositionOneEnd = raf.readInt();
			int lastViewOffsetOne = raf.readInt();

			LanguageAccess laOne = new LanguageAccess(file, languageOne,
					superIndexOneBegin, superIndexOneEnd,
					indexPositionOneBegin, indexPositionOneEnd,
					lastViewOffsetOne);
			languages.add(laOne);

			// Language Two
			int languageTwoSize = raf.readInt();
			byte[] bufTwo = new byte[languageTwoSize];
			raf.read(bufTwo, 0, languageTwoSize);
			String languageTwo = new String(bufTwo, 0, languageTwoSize,
					DatabaseAccess.CHARSET_DEFAULT);
			int superIndexTwoBegin = raf.readInt();
			int superIndexTwoEnd = raf.readInt();
			int indexPositionTwoBegin = raf.readInt();
			int indexPositionTwoEnd = raf.readInt();
			int lastViewOffsetTwo = raf.readInt();

			LanguageAccess laTwo = new LanguageAccess(file, languageTwo,
					superIndexTwoBegin, superIndexTwoEnd,
					indexPositionTwoBegin, indexPositionTwoEnd,
					lastViewOffsetTwo);
			languages.add(laTwo);

			int recordsBegin = raf.readInt();
			int recordsEnd = raf.readInt();
			records = new RecordAccess(recordsBegin, recordsEnd);

		} catch (IOException e) {
			throw new DatabaseAccessException(e);
		}
	}

	public class RecordAccess {
		public final static byte RECORD_HEADER = 0x06;
		private RandomAccessFile raf;
		private int recordsBegin;
		private int recordsEnd;

		public RecordAccess(int recordsBegin, int recordsEnd)
				throws DatabaseAccessException {
			try {
				this.raf = new RandomAccessFile(ContentAccess.this.file, "r");
			} catch (FileNotFoundException e) {
				throw new DatabaseAccessException( ErrorCode.ERROR_CANNOT_READ_FILE );
			}
			this.recordsBegin = recordsBegin;
			this.recordsEnd = recordsEnd;
		}

		public void close() throws DatabaseAccessException {
			try {
				raf.close();
			} catch (IOException e) {
				throw new DatabaseAccessException(e);
			}
		}
		
		public RecordIterator getIterator() throws DatabaseAccessException {
			return new RecordIterator( raf, recordsBegin, recordsEnd );
		}

		public String getAtPosition(int position)
				throws DatabaseAccessException {
			String record;
			try {
				if ( position != raf.getChannel().position())
					raf.seek(position);

				byte header = raf.readByte();
				if (header != RECORD_HEADER)
					throw new DatabaseAccessException(
							ErrorCode.ERROR_CANNOT_FIND_RECORD_HEADER );
				int recordSize = raf.readInt();
				byte[] buf = new byte[recordSize];
				raf.read(buf, 0, recordSize);
				record = new String(buf, 0, recordSize,
						DatabaseAccess.CHARSET_DEFAULT);
			} catch (IOException e) {
				throw new DatabaseAccessException( e );
			}
			return record;
		}

		public class RecordIterator {
			private RandomAccessFile raf;
			private int recordsBegin;
			private int recordsEnd;
			private int crrRecordPosition;

			public RecordIterator(RandomAccessFile raf, int recordsBegin,
					int recordsEnd) throws DatabaseAccessException {
				this.raf = raf;
				this.recordsBegin = recordsBegin;
				this.recordsEnd = recordsEnd;
				crrRecordPosition = recordsBegin;
				try {
					raf.seek(this.recordsBegin);
				} catch (IOException e) {
					throw new DatabaseAccessException(e);
				}
			}

			public boolean hasNext() {
				return crrRecordPosition < recordsEnd;
			}

			public String next() throws DatabaseAccessException {
				String record;
				if (hasNext()) {
					try {
						byte header = raf.readByte();
						if (header != RECORD_HEADER)
							throw new DatabaseAccessException(
									ErrorCode.ERROR_CANNOT_FIND_RECORD_HEADER );
						int recordSize = raf.readInt();
						byte[] buf = new byte[recordSize];
						raf.read(buf, 0, recordSize);
						record = new String(buf, 0, recordSize,
								DatabaseAccess.CHARSET_DEFAULT);
						crrRecordPosition = (int) raf.getChannel().position();
					} catch (IOException e) {
						throw new DatabaseAccessException(e);
					}
					return record;
				}
				throw new IndexOutOfBoundsException( "Can't seek past existing database records.");
			}
		}
	}

}
